#ifndef GAMEOBJECT_H
#define GAMEOBJECT_H

#include <string>
#include <vector>
#include <memory>
#include <typeinfo>
#include <typeindex>
#include <unordered_map>
#include "../Collision/collision.h"

// Forward declarations
class Component;
class Transform;
class Renderer;
class TextureRenderer;
class Rigidbody;
class Camera;

// Game object class
class GameObject {
public:
    GameObject(const std::string& name = "GameObject");
    virtual ~GameObject();

    // Basic properties
    const std::string& getName() const { return name_; }
    void setName(const std::string& name) { name_ = name; }

    bool isActive() const { return active_; }
    void setActive(bool active) { active_ = active; }    // Component management
    template<typename T, typename... Args>
    T* addComponent(Args&&... args);

    template<typename T>
    T* getComponent();

    template<typename T>
    bool removeComponent();

    // Update and rendering
    virtual void update(float deltaTime);
    virtual void render();

    // Collision detection
    void setCollisionBox(std::shared_ptr<CollisionBox> collisionBox);
    std::shared_ptr<CollisionBox> getCollisionBox() const;
    bool checkCollision(const GameObject& other) const;

    // Shortcut to get Transform component
    Transform* getTransform() const { return transform_; }

private:
    std::string name_;
    bool active_;
    std::vector<std::unique_ptr<Component>> components_;
    std::unordered_map<std::type_index, Component*> componentMap_;
    std::shared_ptr<CollisionBox> collisionBox_;
    Transform* transform_; // Fast access to Transform component
};

// Component base class
class Component {
public:
    Component(GameObject* gameObject) : gameObject_(gameObject), enabled_(true) {}
    virtual ~Component() = default;

    bool isEnabled() const { return enabled_; }
    void setEnabled(bool enabled) { enabled_ = enabled; }

    GameObject* getGameObject() const { return gameObject_; }

    virtual void update(float deltaTime) {}
    virtual void render() {}

protected:
    GameObject* gameObject_;
    bool enabled_;
};

// Transform component
class Transform : public Component {
public:
    Transform(GameObject* gameObject);

    // Position
    float getX() const { return x_; }
    float getY() const { return y_; }
    void setPosition(float x, float y);
    void translate(float deltaX, float deltaY);

    // Size
    float getWidth() const { return width_; }
    float getHeight() const { return height_; }
    void setSize(float width, float height);

    // Rotation
    float getRotation() const { return rotation_; }
    void setRotation(float rotation) { rotation_ = rotation; }

private:
    float x_, y_;
    float width_, height_;
    float rotation_;
};

// Renderer component
class Renderer : public Component {
public:
    Renderer(GameObject* gameObject);

    void setColor(int color) { color_ = color; }
    int getColor() const { return color_; }

    void render() override;

    // Static method to set current rendering camera
    static void setRenderingCamera(Camera* camera) { s_renderingCamera = camera; }
    static Camera* getRenderingCamera() { return s_renderingCamera; }

    static void setDirection(int direction) { s_direction = direction; }

private:
    int color_;
    static Camera* s_renderingCamera;
    static int s_direction;
};

// Rigidbody component (simple physics)
class Rigidbody : public Component {
public:
    Rigidbody(GameObject* gameObject);

    // Velocity
    float getVelocityX() const { return velocityX_; }
    float getVelocityY() const { return velocityY_; }
    void setVelocity(float vx, float vy);
    void addVelocity(float vx, float vy);

    // Previous position (for continuous collision detection)
    float getPreviousX() const { return prevX_; }
    float getPreviousY() const { return prevY_; }

    // Gravity
    bool useGravity() const { return useGravity_; }
    void setUseGravity(bool use) { useGravity_ = use; }

    // Ground detection
    bool isGrounded() const { return grounded_; }
    void setGrounded(bool grounded) { grounded_ = grounded; }

    void update(float deltaTime) override;

    static const float GRAVITY;

private:
    float velocityX_, velocityY_;
    float prevX_, prevY_;  // Previous frame position
    bool useGravity_;
    bool grounded_;
};

// Template implementation
template<typename T, typename... Args>
T* GameObject::addComponent(Args&&... args) {
    std::type_index typeIndex(typeid(T));

    // Check if this type of component already exists
    if (componentMap_.find(typeIndex) != componentMap_.end()) {
        return static_cast<T*>(componentMap_[typeIndex]);
    }

    auto component = std::make_unique<T>(this, std::forward<Args>(args)...);
    T* componentPtr = component.get();

    componentMap_[typeIndex] = componentPtr;
    components_.push_back(std::move(component));

    return componentPtr;
}

template<typename T>
T* GameObject::getComponent() {
    std::type_index typeIndex(typeid(T));
    auto it = componentMap_.find(typeIndex);
    if (it != componentMap_.end()) {
        return static_cast<T*>(it->second);
    }
    return nullptr;
}

template<typename T>
bool GameObject::removeComponent() {
    std::type_index typeIndex(typeid(T));
    auto it = componentMap_.find(typeIndex);
    if (it == componentMap_.end()) {
        return false;
    }

    Component* componentToRemove = it->second;
    componentMap_.erase(it);

    // Remove from vector
    components_.erase(
        std::remove_if(components_.begin(), components_.end(),
            [componentToRemove](const std::unique_ptr<Component>& comp) {
                return comp.get() == componentToRemove;
            }),
        components_.end()
    );

    return true;
}

#endif // GAMEOBJECT_H
